/*
 * Description: 用来缓存所有的调用，当调用返回时，会从缓存中查找对应的调用。
 */
#include "call_records_cache.h"
#include "error.h"
#include "log.h"
#include <iostream>
#include <windows.h>

namespace COMMON {
CallRecordsCache::~CallRecordsCache()
{
    while (waitNum > 0) {
        Sleep(5); /* 5 等待5ms,保证锁退出 */
    }
}

void CallRecordsCache::AddCallRecord(uint32_t callID, google::protobuf::MessageLite *result)
{
    auto callRecord = std::make_unique<CallRecord>(result);
    std::unique_lock<std::mutex> lk(m_mutex);
    m_caches[callID] = std::move(callRecord);
}

void CallRecordsCache::RemoveCallRecord(uint32_t callID)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto it = m_caches.find(callID);
    if (it != m_caches.end() && it->second != nullptr) {
        it->second->WakeUp();
        m_caches.erase(callID);
    } else {
        LOG_ERR("wake up an nonexistent call");
    }
}

int CallRecordsCache::WaitForResponse(uint32_t callID)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    waitNum++;
    auto it = m_caches.find(callID);
    if (it == m_caches.end() || it->second == nullptr) {
        waitNum--;
        return DEFAULT_ERROR;
    }
    std::unique_ptr<CallRecord> &callrecord = it->second;
    lk.unlock();
    callrecord->WaitForResponse();
    int errorCode = callrecord->GetErrorCode();
    if (errorCode != OK) {
        LOG_ERR("response result error");
    }
    RemoveCallRecord(callID);
    waitNum--;
    return errorCode;
}

void CallRecordsCache::WakeUp(uint32_t callID)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto it = m_caches.find(callID);
    if (it != m_caches.end() && it->second != nullptr) {
        it->second->WakeUp();
    } else {
        LOG_ERR("woke up an nonexistent call");
    }
}

void CallRecordsCache::WakeUpAll()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    for (auto &it : m_caches) {
        if (it.second != nullptr) {
            it.second->WakeUp();
        }
    }
    m_caches.clear();
}

void CallRecordsCache::SetResult(const Guest::protobuf::link::Result &result)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    auto it = m_caches.find(result.callid());
    if (it != m_caches.end() && it->second != nullptr) {
        it->second->SetResult(result);
    } else {
        LOG_ERR("set result for an nonexistents call");
    }
}
}
